﻿/*
			       ___ _      _   ___ _  _    ___  _  _ 
			      | __| |    /_\ / __| || |  / _ \| \| |
			      | _|| |__ / _ \\__ \ __ | | (_) | .` |
			      |_| |____/_/ \_\___/_||_|  \___/|_|\_|
			                                            
			  _____ _  _   _ _____   ___ ___ _____ ___ _  _ 
			 |_   _| || | /_\_   _| | _ )_ _|_   _/ __| || |
			   | | | __ |/ _ \| |   | _ \| |  | || (__| __ |
			   |_| |_||_/_/ \_\_|   |___/___| |_| \___|_||_|
			        F L A S H  O N  T H A T  B I T C H
			        
		+--------------------------------------------------------+
		|	This file is delivered as is. Any modifications to   |
		|   this class and/or sub classes may cause errors and   |
		|   can cause you compilations to fail. FOTB takes no	 |
		|   responsibility for any complications caused by the	 |
		|   following code or any modified versions of it.		 |
		+--------------------------------------------------------+

*/

package com.flashonthatbitch.filesystem.parsers {

/*
		 ___ __  __ ___  ___  ___ _____ 
		|_ _|  \/  | _ \/ _ \| _ \_   _|
		 | || |\/| |  _/ (_) |   / | |  
		|___|_|  |_|_|  \___/|_|_\ |_|  
		I M P O R T
*/
	
	import com.flashonthatbitch.filesystem.FileObject;
	import com.flashonthatbitch.filesystem.FileReadMode;
	import flash.utils.ByteArray;
	import flash.utils.Endian;

	public class ParserPrototype {
		
		private var _file:FileObject;
		
		protected var bytearray:ByteArray;
		protected var bit_left:uint = 0;
		protected var bit_buffer:uint = 0;
		
/*
		  ___ ___  _  _ ___ _____ ___ _   _  ___ _____ ___  ___ 
		 / __/ _ \| \| / __|_   _| _ \ | | |/ __|_   _/ _ \| _ \
		| (_| (_) | .` \__ \ | | |   / |_| | (__  | || (_) |   /
		 \___\___/|_|\_|___/ |_| |_|_\\___/ \___| |_| \___/|_|_\
		C O N S T R U C T O R
*/
		
		/**
		 * Creates a new  ParserPrototype object.
		 * 
		 * @param	fileObject		FileObject to parse
		 */
		public function ParserPrototype(fileObject:FileObject=null) {
			
			if (fileObject)
				file = fileObject;
			
		}
		
		/**
		 * [internal-use]
		 * Override this function!
		 */
		protected function parse():void {
			
		}
		
		/**
		 * [internal-use]
		 * Flush/Clear bitstream.
		 */
		protected function flushBits():void{
			bit_buffer = bit_left = 0;
		}
		
/*
		  ___ ___ _____   ___ ___ _____ 
		 / __| __|_   _| / __| __|_   _|
		| (_ | _|  | |   \__ \ _|  | |  
		 \___|___| |_|   |___/___| |_|  
		G E T  S E T
*/
		/**
		 * Sets byteorder to lowest significant bytes first.
		 */
		protected function get little_endian():String { return bytearray.endian = Endian.LITTLE_ENDIAN;		}
		/**
		 * Sets byteorder to most significant bytes first.
		 */
		protected function get big_endian():String { return bytearray.endian = Endian.BIG_ENDIAN;			}
		/**		
		 * Reads an signed 32bit integer
		 */
		protected function get int32():int 		{ return bytearray.readInt();							}
		/**		
		 * Reads an signed 16bit integer
		 */
		protected function get int16():int		{ return bytearray.readShort()							}
		/**		
		 * Reads an unsigned 8bit integer
		 */
		protected function get uint8():uint 		{ return bytearray.readByte(); 							}
		/**		
		 * Reads an unsigned 16bit integer
		 */
		protected function get uint16():uint 	{ return bytearray.readUnsignedShort();					}
		/**		
		 * Reads an unsigned 24bit integer
		 */
		protected function get uint24():uint 	{ return bytearray[bytearray.position++]<<16 | bytearray[bytearray.position++]<<8 | bytearray[bytearray.position++]; }
		/**		
		 * Reads an unsigned 32bit integer
		 */
		protected function get uint32():uint 	{ return bytearray.readUnsignedInt(); 					}
		/**		
		 * Reads a number
		 */
		protected function get num():Number 		{ return bytearray.readDouble();							}
		/**		
		 * Reads a 8bit boolean value.
		 */
		protected function get bool():Boolean	{ return bytearray.readByte() == 0x01;					}
		/**		
		 * Reads a UTF string (first byte is length specific)
		 */
		protected function get str():String 		{ return bytearray.readUTFBytes(bytearray.readByte());		}
		/**		
		 * Reads a float value.
		 */
		protected function get float():Number 	{ return bytearray.readFloat();							}
		
		/**
		 * Reads a fixed point 8.8bit number
		 */
		protected function get fixed8():Number {
			flushBits();
			bit_buffer = bytearray.readUnsignedShort();
			var n:Number = Number( ((bit_buffer >> 8) & 0xff) * 1.0 + ((bit_buffer & 0xff) * 0.1 ) );
			bit_buffer = 0;
			return n;
		}
		
		/**
		 * Reads signed bits
		 * 
		 * @param	length
		 * @return
		 */
		protected function readBits(length:uint):int {
			var num:int = readUnsignedBits(length);
			var shift:int = 32-length;
			num = (num << shift) >> shift;			
			return num;
		}
		
		/**
		 * Reads unsigned bits
		 * 
		 * @param	length
		 * @return
		 */
		protected function readUnsignedBits(length:uint):uint {
			if ( length > 64 || length == 0 ) return 0;
			var r:uint = (bit_buffer >> (8 - bit_left));
			if ( bit_left >= length ){
				bit_left -= length;
				bit_buffer <<= length;
				return (r >> (8-length));
			}
			length -= bit_left;
			while ( length>7 ){
				bit_buffer = bytearray.readUnsignedByte();
				r = ( r<<8 ) | bit_buffer;
				length -= 8;
				bit_left = 0;
			}
			bit_buffer = 0;
			if ( length ){
				bit_buffer = bytearray.readUnsignedByte();
				bit_left = 8-length;
				r = (r<<length)|(bit_buffer>>bit_left);
				bit_buffer <<= length;
			}
			bit_buffer &= 0xff;
			return r;
		}
		
		/**
		 * Retrieves the previously specified FileObject.
		 */
		public function get file():FileObject { return _file; }
		
		/**
		 * Sets a FileObject and starts the parsing process.
		 */
		public function set file(value:FileObject):void {
			_file = value;
			bytearray = _file.read(FileReadMode.READ_BYTES);
			parse();
		}
		
	}

}